#include "Moussavi_Internals.h"

extern uint8_t g_iCurrentFrameCycle;
static U8 msg[MAX_MSG_SIZE];
static U8 *msg_ptr;

volatile uint8_t 	g_leftDataToGet 	= 0;
volatile uint32_t 	g_dataStartAddress 	= 0;  
volatile uint8_t	g_EchoOnOff = 1;
volatile uint8_t	g_isOperating = 1;

static cmd_t cmd_tbl[] = 
{
	{"idn?",			tm_cmd_idn,					""					},
	{"repa",			tm_cmd_repa,				""					},
	{"read",			tm_cmd_read,				"[st] [len]"		},
    {"write",			tm_cmd_write,				"[st] [len]"		},
	{"write_blk",		tm_cmd_write_block,			"[st_blk] [len]"	},
	{"cs_low",			tm_cmd_cs_low,				""					},
	{"cs_high",			tm_cmd_cs_high,				""					},
	{"spi_trans",		tm_cmd_spi_transfer,		"[u8]"				},
	{"unprot_flash",	tm_cmd_unprotect_flash,		"[blk#/all]"		},
	{"prot_flash",		tm_cmd_protect_flash,		"[blk#/all]"		},
	{"erase_flash",		tm_cmd_erase_flash,			"[blk#/all]"		},
	{"flash_stat",		tm_cmd_flash_status,		""					},
	{"dir",				tm_cmd_dir,					""					},
	{"get_fs",			tm_cmd_get_fs,				""					},
	{"test_led",		tm_cmd_test_led,			"[row#] [rgb]"		},
	{"mem_test",		tm_cmd_test_memory,			""					},
	{"help",			tm_cmd_help,				"[cmd]"				},
	{"sw_reset",		tm_cmd_software_reset,		""					},
	{"eof",				tm_cmd_echo_off,			""					},
	{"eon",				tm_cmd_echo_on,				""					},
	{"stop_all", 		tm_cmd_stop_all,			""					},
	{"start_all",		tm_cmd_start_all, 			""					},
	{NULL,      		NULL,						NULL				}
};


/**************************************************************************/
/*!
    This is the rx handler function. Collecting transmitted (rx) command
	and echoing it back. As the cmd is finished transmitting, it sends it
	to the parser
*/
/**************************************************************************/
void rx()
{
    U8 i, c, ep_num, len;
    usb_pcb_t *pcb = usb_pcb_get();
	uint8_t j;

    // get the ep number of any endpoint with pending rx data
    if ((ep_num = usb_buf_data_pending(DIR_OUT)) != 0xFF)
    {
        // get the length of data in the OUT buffer
        len = pcb->fifo[ep_num].len;

		if (g_leftDataToGet)
		{ 
			for (j=0; j<len; j++)
			{
				c = usb_buf_read(ep_num);
				dataflash_write ( g_dataStartAddress++, c );

				if (g_leftDataToGet) g_leftDataToGet--;
			}

			if ( g_leftDataToGet == 0 )
				printf_P(PSTR("finished\n\r"));

		}
		else 
		{
	        // read out the data in the buffer and echo it back to the host. 
	        for (i=0; i<len; i++)
	        {
	            c = usb_buf_read(ep_num);

	            switch (c)
	            {
				case ':':
	            case '\r':
	                // terminate the msg and reset the msg ptr. then send
	                // it to the handler for processing.
					if (g_isOperating != 0)
					{
						tm_cmd_stop_all(0, NULL);
					}

	                *msg_ptr = '\0';

					if (g_EchoOnOff)
		                printf_P(PSTR("\n\r"));
	                
					cmd_parse((char *)msg);
	                msg_ptr = msg;
	                break;
            
	            case '\b':
	                usb_buf_write(EP_1, c);
	                if (msg_ptr > msg)
	                {
	                    msg_ptr--;
	                }
	                break;
            
	            default:
					if (g_EchoOnOff)
						usb_buf_write(EP_1, c);

	                *msg_ptr++ = c;
	                break;
	            }
	        }
	        pcb->flags |= (1 << TX_DATA_AVAIL);
	    }
	}
}


/**************************************************************************/
/*!
	This is the command prompt shell line printer command    
*/
/**************************************************************************/
static void cmd_menu( void )
{
	if ( !g_leftDataToGet && g_EchoOnOff==1)
	{
    	printf_P(PSTR("\n"));
    	printf_P(PSTR(COMMAND_PROMPT));
	}
}

/**************************************************************************/
/*!
	echo is off    
*/
/**************************************************************************/
void tm_cmd_echo_off(U8 argc, char **argv)
{
	g_EchoOnOff = 0;
	printf_P (PSTR("ok"));	
}

/**************************************************************************/
/*!
	Enterred the transfer mode so stop all the other stuff
*/
/**************************************************************************/
void tm_cmd_stop_all(U8 argc, char **argv)
{
	g_isOperating = 0;

	TIMSK1 = 0;	// Compare Interrupt
	TIMSK2 = 0; // Compare interrupt

	ROW_PORT = 0x00;

	CS_UP;

	printf_P (PSTR("ok"));
}

/**************************************************************************/
/*!
	Exitted the transfer mode so start up all the other stuff
*/
/**************************************************************************/
void tm_cmd_start_all(U8 argc, char **argv)
{
	g_isOperating = 1;

	TIMSK1 = (1<<OCIE1A);	// Compare Interrupt
	TIMSK2 = (1<<OCIE2A); 	// Compare interrupt
	SET_ROW(0);

	CS_UP;
	g_iCurrentFrameCycle = 0;

	
	printf_P (PSTR("ok"));
}



/**************************************************************************/
/*!
	echo is on
*/
/**************************************************************************/
void tm_cmd_echo_on(U8 argc, char **argv)
{
	g_EchoOnOff = 1;
	printf_P (PSTR("ok"));	
}

/**************************************************************************/
/*!
	This function parses the command (recieved in void rx()) and tries
	to find a suitable command line in the cmd_table. Then invokes the
	appropriate function given in the cmd_table.    
*/
/**************************************************************************/
void cmd_parse(char *cmd)
{
    U8 argc, i = 0;
    char *argv[30];

    fflush(stdout);

    argv[i] = strtok(cmd, " ");
    do
    {
        argv[++i] = strtok(NULL, " ");
    } while ((i < 30) && (argv[i] != NULL));
    
    argc = i;
    for (i=0; cmd_tbl[i].cmd != NULL; i++)
    {
        if (!strcmp(argv[0], cmd_tbl[i].cmd))
        {
            cmd_tbl[i].func(argc, argv);
            cmd_menu();
            return;
        }
    }
    printf_P(PSTR("CMD: Command not recognized.\n"));

    cmd_menu();
}


/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_idn				(U8 argc, char **argv)
{
	char ManID, DevID1, DevID2, ExData;

	printf_P ( PSTR("MOUSSAVI VERSION:%03d.%d;"			// The project
					"MEM_SIZE:%05d MBITS;"				// Memory Size
					"CLOCK:%03d MHZ;"					// CPU SIZE
					"#LEDS:%03d;"						// number of leds
					"RGB:%d;" 							// RGB?
					"MEMBLK:%02d KB"					// Flash minimal block
					"\r\n"),
					MOUSSAVI_VER,
					MOUSSAVI_SUBVER,
					MOUSSAVI_MEMORY_SIZE,
					MOUSSAVI_CLOCK_SPEED,
					MOUSSAVI_NUM_LEDS,
					MOUSSAVI_IS_RGB,
					FLASH_BLOCK4_SIZE );

	printf_P ( PSTR("\r\nFlashInfo:\r\n") );
	dataflash_spi_idn ( &ManID, &DevID1, &DevID2, &ExData );

	printf_P ( PSTR("ManID = 0x%x; DevID1 = 0x%x; DevID2 = 0x%x; ExData = 0x%x\r\n"),
					ManID, DevID1, DevID2, ExData );

}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_repa			(U8 argc, char **argv)
{
	printf_P ( PSTR("COLOR_DEPTH:%03d;"
					"FILE#BLKS:%02d;"
					"FN_LEN:%03d;"
					"MAX_FRAMES:%03d;\r\n"), 
				MOUSSAVI_COLOR_DEPTH,
				FS_FRAME_BLOCKS,
				FS_FILENAME_LENGTH,
				FS_FILE_MAX_FRAMES);
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_read			(U8 argc, char **argv)
{
	if ( argc != 3 )
	{
		printf_P(PSTR("usage: read [start_address] [length]\n"));
		return;	
	}
	else
	{
		uint32_t iIterator;
		uint32_t iStart = atol (argv[1]);
		uint32_t iLength = atol (argv[2]);

		printf_P (PSTR("reading: %lu to %lu;\r\n"), iStart, iStart+iLength);

		for (iIterator=0; iIterator<=iLength; iIterator ++ )
			printf_P(PSTR("A%lu:   0x%x\r\n"), (iStart+iIterator), 
									dataflash_read (iStart+iIterator) );
	}
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_write			(U8 argc, char **argv)
{
	if ( argc != 3 )
	{
		printf_P(PSTR("usage: write [32bit start_address] [8bit length]\n"));
		return;	
	}
	else
	{
		g_dataStartAddress = atol(argv[1]);
		g_leftDataToGet = atoi(argv[2]);

		if ( g_leftDataToGet > 15 ) g_leftDataToGet = 15;	// Don't allow more then 15 bytes

		printf_P (PSTR("writing: %lu to %lu;\r\n"), g_dataStartAddress, 
													g_dataStartAddress+g_leftDataToGet);

//		for (iIterator=0; iIterator<=iLength; iIterator ++ )
//			dataflash_write ( iStart+iIterator, 1/*USARTReadChar()*/ );
	}
}


/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_write_block		(U8 argc, char **argv)
{
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_cs_low			(U8 argc, char **argv)
{
	CS_DOWN;
	printf_P (PSTR("ok"));
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_cs_high			(U8 argc, char **argv)
{
	CS_UP;
	printf_P (PSTR("ok"));
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_spi_transfer	(U8 argc, char **argv)
{
	if ( argc != 2)
	{
		printf_P(PSTR("usage: spi_trans [byte]\n"));
		return;		
	}
	else
	{
		U8 byte_sent;
		U8 byte_rec;

		byte_sent = atoi (argv[1]);	

		printf_P(PSTR("\r\nSending 0x%x"), byte_sent );
		byte_rec = spi_transfer(byte_sent);

		printf_P(PSTR("\r\nRecieved 0x%x\r\n"), byte_rec );
	}
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_unprotect_flash	(U8 argc, char **argv)
{
	if ( argc != 2 )
	{
		printf_P(PSTR("usage: unprot_flash [<block number> or <ALL>]\n"));
		return;		
	}
	else
	{
		if ( strcmp(argv[1], "ALL") == 0)
		{
			dataflash_unprotect_all ( );
		}
		else
		{
			uint32_t iBlock;
			uint32_t iAddress;

			iBlock = atol (argv[1]);						// This is the data

			// Calculate address
			iAddress = iBlock * (uint32_t)(FLASH_BLOCK4_SIZE);

			dataflash_unprotect_block4 ( iAddress );
		}
	}
	printf_P (PSTR("ok"));
}


/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_protect_flash	(U8 argc, char **argv)
{
	if ( argc != 2 )
	{
		printf_P(PSTR("usage: prot_flash [<block number> or <ALL>]\n"));
		return;		
	}
	else
	{
		if ( strcmp(argv[1], "ALL") == 0)
		{
			dataflash_protect_all ( );
		}
		else
		{
			uint32_t iBlock;
			uint32_t iAddress;

			iBlock = atol (argv[1]);						// This is the data

			// Calculate address
			iAddress = iBlock * (uint32_t)(FLASH_BLOCK4_SIZE);

			dataflash_protect_block4 ( iAddress );
		}
	}
	printf_P (PSTR("ok"));
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_erase_flash		(U8 argc, char **argv)
{
	if ( argc != 2 )
	{
		printf_P(PSTR("usage: erase_flash [<block number> or <ALL>]\n"));
		return;		
	}
	else
	{
		if ( strcmp(argv[1], "ALL") == 0)
		{
			dataflash_erase_all ( );
		}
		else
		{
			uint32_t iBlock;

			iBlock = atol (argv[1]);						// This is the data
			
			dataflash_unprotect_block4 ( (uint16_t)(iBlock) );

			dataflash_erase_block4 ( (uint16_t)(iBlock) );
		}
	}
	printf_P (PSTR("ok"));
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_flash_status	(U8 argc, char **argv)
{
	uint8_t stat_flash = dataflash_read_status ( );

	if (stat_flash&FLASH_READY)
		printf_P(PSTR("\r\n#0 NOT ready (1)") );
	else
		printf_P(PSTR("\r\n#0 ready (0)") );

	if (stat_flash&FLASH_WE)
		printf_P(PSTR("\r\n#1 write enabled (1)") );
	else
		printf_P(PSTR("\r\n#1 NOT write enabled (0)") );


	if ((stat_flash&FLASH_SOFTWARE_PROTECT)==0x1100)
		printf_P(PSTR("\r\n#23 write protected (11)") );
	else if ((stat_flash&FLASH_SOFTWARE_PROTECT)==0x0100)
		printf_P(PSTR("\r\n#23 partly write protected (01)") );
	else if ((stat_flash&FLASH_SOFTWARE_PROTECT)==0x0000)
		printf_P(PSTR("\r\n#23 NOT write protected (00)") );
	else
		printf_P(PSTR("\r\n#23 protection unknown (10 reserved)") );

	if (stat_flash&FLASH_WP_PIN)
		printf_P(PSTR("\r\n#4 NOT write protected (1)") );
	else
		printf_P(PSTR("\r\n#4 write protected (0)") );

	if (stat_flash&FLASH_ERASE_WRITE_ERR)
		printf_P(PSTR("\r\n#5 failed program (erase) (1)") );
	else
		printf_P(PSTR("\r\n#5 program (erase) was successfull (0)") );

	if (stat_flash&FLASH_SEC_PROTECT_VEC_LOCK)
		printf_P(PSTR("\r\n#7 sector protection registers LOCKED (1)\r\n\r\n") );
	else
		printf_P(PSTR("\r\n#7 sector protection registers UNLOCKED (0)\r\n\r\n") );
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_dir				(U8 argc, char **argv)
{
	FS_ShowFileInformation ( );
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_get_fs			(U8 argc, char **argv)
{
	FileEntry_ST sCurEntry;
	char *cEntry = NULL;
	uint32_t iAddress = ENTRY_TABLE_ADDR;
	uint8_t	itr = 0;

	// Try reading entries until the end
	while ( FS_ReadEntry ( &iAddress, &sCurEntry ) )
	{
		cEntry = (char*)(&sCurEntry);
		
		for ( itr = 0; itr < ENTRY_TABLE_EL_SIZE; itr ++ )
			putchar( cEntry[itr] );		
	}

	if ( itr == 0 )		// nothing was read then FS empty
	{
		printf_P(PSTR("EMPTY\r\n") );
	}
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_test_led		(U8 argc, char **argv)
{
	if ( argc != 3 )
	{
		printf_P(PSTR("usage: test_led [row#] [RGB]\n"));
		return;	
	}
	else
	{
		U8 iLastRowPort = ROW_PORT;
		U8 i;

		// Set the row num
		ROW_PORT = (1<<atoi(argv[1]));
	
		for (i = 0; i<strlen(argv[2]); i++)
		{
			if (argv[2][i]=='R')
			{
				DDRB |= 0xF0;
				DDRF |= 0xF0;
			}

			if (argv[2][i]=='G')
			{
				DDRD |= 0b00111100;
				DDRC |= 0x0F;
			}

			if (argv[2][i]=='B')
			{
				DDRF |= 0x0F;
				DDRC |= 0xF0;
			}
		}

		_delay_ms(1500);

		DDRB &= 0x0F;
		DDRC = 0;
		DDRD &= 0b11000011;
		DDRF = 0;
	
		// Cleanup
		ROW_PORT = iLastRowPort;
	} 
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_test_memory		(U8 argc, char **argv)
{
	uint32_t ErrCounter = 0;
	uint32_t pos = 0;	
	uint8_t retVal = 0, Val = 0;

	// Erase memory
	printf_P ( PSTR("Deleting all information from flash...\r\n") );
	
	dataflash_erase_all ( );

	printf_P ( PSTR("Unprotecting entire flash...\r\n") );

	dataflash_unprotect_all ( );

#ifndef __NOSLEEP_NOSPI_DEBUG
	_delay_ms(4000);
#endif

	printf_P ( PSTR("Writing testing sequence...\r\n") );
	for (pos = 0; pos < FS_MEMORY_SIZE; pos ++)
	{
		Val = pos&0xff;

		if ( (pos & 0xFFF) == 0)	// Every 4096
			printf_P ( PSTR("%lu "), pos);

		dataflash_write	( pos, Val );
	}

#ifndef __NOSLEEP_NOSPI_DEBUG
	_delay_ms(10000);
#endif
	printf_P ( PSTR("Reading the testing sequence..\r\n") );

	for (pos = 0; pos < FS_MEMORY_SIZE; pos ++)
	{
		retVal = dataflash_read	( pos );
		Val = pos&0xff;

		if ( (pos & 0xFFF) == 0)	// Every 4096
			printf_P ( PSTR("%lu "), pos);
		
		if (retVal!=Val)
			ErrCounter ++;
	}

	printf_P ( PSTR("Number of errors: %lu\r\n"), ErrCounter);
}

/**************************************************************************/
/*!
	This function shows the help menu to the user
*/
/**************************************************************************/
void tm_cmd_help			(U8 argc, char **argv)
{
	if (argc > 2)
	{
		printf_P(PSTR("usage: help [cmd]\n"));
		return;	
	}
	else if (argc == 2)
	{
		U8 i = 0;
		while (	cmd_tbl[i].cmd != NULL && 
				strcmp(cmd_tbl[i].cmd, argv[1]) != 0 ) 
			{ i++; }
		
		if ( cmd_tbl[i].cmd != NULL )
		{
			printf_P(PSTR("usage: %s %s\n"), cmd_tbl[i].cmd, 
					((strlen(cmd_tbl[i].usage)==0)?"no parameters":cmd_tbl[i].usage));	
		}	
	}
	else
	{
		U8 i = 0;
		while (	cmd_tbl[i].cmd != NULL ) 
		{ 
			printf_P(PSTR("%s: %s\n"), cmd_tbl[i].cmd, 
				((strlen(cmd_tbl[i].usage)==0)?"no parameters":cmd_tbl[i].usage));
			i++; 
		}
	}
}

/**************************************************************************/
/*!
	TBD   
*/
/**************************************************************************/
void tm_cmd_software_reset	(U8 argc, char **argv)
{
	cli(); //irq's off 
	wdt_enable(WDTO_15MS); //wd on,15ms 
	while(1); //loop
}


